// if (ll.listPlugins().indexOf("TMEssential.js") == -1) {
//     ll.require("TMEssential.js");
// }

/**
 * @note 此文件为TMET全API简单调用依赖!
 * @note 可以使用此文件进行简单操作，例如: 
 */

/**
 * TPA信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class TPAInfo {
    constructor() { }
    /** 发起者 
     * @type {string} 
     */
    get from() { }
    /** 接收者 
     * @type {string} 
     */
    get to() { }
    /** 发起时间戳 
     * @type {number} 
     */
    get time() { }
    /** 是否是tpahere 
     * @type {boolean} 
     */
    get isHereMode() { }
}

/**
 * 坐标信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class PosInfo {
    constructor() { }
    /** @type {number} */
    get x() { }
    /** @type {number} */
    get y() { }
    /** @type {number} */
    get z() { }
    /** @type {number} */
    get dimid() { }
}

/**
 * 商品信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class CommodityInfo {
    constructor() { }
    /** 商品标准名（minecraft:） 
     * @type {string} 
     */
    get type() { }
    /** 商品特殊值 
     * @type {number} 
     */
    get aux() { }
    /** 商品备注 
     * @type {string}
     */
    get remark() { }
    /** 一个商品的价格 
     * @type {number}
     */
    get money() { }
}

/**
 * 账单信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class HistoryInfo {
    constructor() { }
    /** 发起者(就是获取人)xuid 
     * @type {string} 
     */
    get from() { }
    /** 接收者xuid(可能为空字符串) 
     * @type {string} 
     */
    get to() { }
    /** 操作金额(可能为负数) 
     * @type {number} 
     */
    get money() { }
    /** 操作时间戳(秒) 
     * @type {number} 
     */
    get time() { }
    /**
     * 转账附加说明
     * @type {string}
     */
    get note() { }
}

/**
 * 语言包信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class LangPackInfo {
    constructor() { }
    /** 语言包名称 
     * @type {string} 
     */
    get name() { }
    /** 语言包作者(可能未定义) 
     * @type {(string|null)} 
     */
    get author() { }
    /** 语言包说明(可能为定义) 
     * @type {(string|null)} 
     */
    get description() { }
}

/**
 * 公告信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class NoticeInfo {
    constructor() { }
    /** 标题 
     * @type {string} 
     */
    get Title() { }
    /** 内容(使用"#&#"换行) 
     * @type {string} 
     */
    get Content() { }
}

/**
 * 死亡信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class DeathInfo extends PosInfo {
    /** 死亡时间(推荐使用new Date(time)获取日期时间对象) 
     * @type {string} 
     */
    get time() { }
}

/**
 * 商店实例(商品)信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class ShopExamInfo {
    constructor() { }
    /** 名称 
     * @type {string} 
     */
    get name() { }
    /** 类型 
     * @type {"exam"} 
     */
    get type() { }
    /** 图片位置(可能为null) 
     * @type {(string|null)} 
     */
    get image() { }
    /** 数据 
     * @type {CommodityInfo} 
     */
    get data() { }
}

/**
 * 商店分类信息
 * 请不要使用class方法!(此class仅注释,不代表实际使用!)
 */
class ShopGroupInfo extends ShopExamInfo {
    constructor() { }
    /** 类型 
     * @type {"group"} 
     */
    get type() { }
    /** 数据 
     * @type {(ShopExamInfo[]|ShopGroupInfo[])} 
     */
    get data() { }
}

/**
 * @type {Map<string,(...args:any[])=>boolean>}
 */
let ImportMap = new Map();

/**
 * @type {Map<string,(...args:any[])=>boolean>}
 */
let A_ImportMap = new Map();

/**
 * @param {string} event 
 * @returns {(...args:any[])=>boolean}
 */
function _IMPORT(event) {
    if (!ImportMap.has(event)) {
        ImportMap.set(event, ll.import("TMETListen", event));
    }
    return ImportMap.get(event);
}

/**
 * @param {string} event 
 * @returns {(...args:any[])=>any}
 */
function _A_IMPORT(name) {
    if (!A_ImportMap.has(name)) {
        A_ImportMap.set(name, ll.import("TMETApi", name));
    }
    return A_ImportMap.get(name);
}

Function.prototype.getName = function () {
    try {
        return (this.name || this.toString().match(/function\s*([^(]*)\(/)[1]);
    } catch (_) {
        return system.randomGuid();
    }
}

function A_SafeCheck(name) {
    if (!ll.hasExported("TMETApi", name)) {
        throw new Error(`安全检查: TMETApi::${name} API没有导出!请检查TMET相关功能是否正常!`);
    }
}

function SafeCheck(pn, nm, e) {
    if (!ll.hasExported("TMETListen", e)) {
        throw new Error(`安全检查: TMETListen::${e} API没有导出!请检查TMET相关功能是否正常!`);
    }
    if (!ll.hasExported(pn, nm)) {
        return true;
    }
    throw new Error(`安全检查: ID: ${pn}::${nm} 已被使用!请不要重复使用同一ID进行远程调用!`);
}

class AsyncOutput {
    constructor() { this._cb = () => { }; };
    _t(...args) {
        return this._cb(...args);
    }
    /**
     * @param {boolean} cb 
     * @returns 
     */
    then(cb) {
        this._cb = cb;
        return true;
    }
}

function FastAddEvent(pluginName, cb, event) {
    let ao = new AsyncOutput();
    setTimeout(() => {
        let res = false;
        try {
            let name = cb.getName();
            SafeCheck(pluginName, name, event);
            ll.export(cb, pluginName, name);
            res = _IMPORT(event)(pluginName, name);
        } catch (e) {
            if (e.message.indexOf("安全检查") != -1) {
                logger.error(e.message);
            } else { throw e; }
        }
        ao._t(res);
    }, 1);
    return ao;
}

let TMETEvent = new class {
    constructor() { };
    /**
     * 切换语言包
     * @param {string} pluginName 
     * @param {(xuid:string|“”,LangPackName:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note xuid 为空字符时为控制台操作
     */
    onSwitchLangPack(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onSwitchLangPack");
    }
    /**
     * 经济变动(不包括付款) type:set 频繁触发
     * @param {string} pluginName 
     * @param {(xuid:string,val:number,type:("sync"|"set"|"init"),note:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note 当type为sync时,将无法拦截此次事件
     */
    onMoneyChange(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onMoneyChange");
    }
    /**
     * 玩家付款(api) LLMONEY 指令付款失效
     * @param {string} pluginName 
     * @param {(fromXuid:string,toXuid:string,val:number,fee:number,note:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onMoneyPay(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onMoneyPay");
    }
    /**
     * tpa请求创建(条件满足)
     * @param {string} pluginName 
     * @param {(fromXuid:string,toXuid:string,isHereMode:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onTPARequest(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPARequest");
    }
    /**
     * tpa请求接受(条件满足)
     * @param {string} pluginName 
     * @param {(xuid,TPAInfo:TPAInfo)=>boolean} cb 请尽量不要使用匿名函数
     */
    onTPAAccept(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPAAccept");
    }
    /**
     * TPA请求拒绝
     * @param {string} pluginName 
     * @param {(xuid,TPAInfo:TPAInfo,isLeft:boolean)=>boolean} cb 请尽量不要使用匿名函数
     */
    onTPADeny(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPADeny");
    }
    /**
     * TPA超时(请尽量不要拦截)
     * @param {string} pluginName 
     * @param {(TPAInfo:TPAInfo)=>boolean} cb 请尽量不要使用匿名函数
     */
    onTPATimeout(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPATimeout");
    }
    /**
     * WARP创建
     * @param {string} pluginName 
     * @param {(xuid:string,warpName:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onWARPAdd(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onWARPAdd");
    }
    /**
     * 前往warp
     * @param {string} pluginName 
     * @param {(xuid:string,warpName:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onWARPGo(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onWARPGo");
    }
    /**
     * 删除warp
     * @param {string} pluginName 
     * @param {(xuid:string|"",warpName:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note xuid 为空字符时为控制台操作
     */
    onWARPDel(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onWARPDel");
    }
    /**
     * 添加家
     * @param {string} pluginName 
     * @param {(xuid:string,homeName:string,isAs:string,asPlayerName:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note isAs代表是否是使用homeas命令，asPlayerName是被操作玩家名称(isAs为false为操作者名称)
     */
    onHomeAdd(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onHomeAdd");
    }
    /**
     * 前往家
     * @param {string} pluginName 
     * @param {(xuid:string,homeName:string,isAs:boolean,asPlayerName:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note isAs代表是否是使用homeas命令，asPlayerName是被操作玩家名称(isAs为false为操作者名称)
     */
    onHomeGo(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onHomeGo");
    }
    /**
     * 删除家
     * @param {string} pluginName 
     * @param {(xuid:string|"",homeName:string,isAs:boolean,asPlayerName:string)=>boolean} cb 请尽量不要使用匿名函数
     * @note isAs代表是否是使用homeas命令，asPlayerName是被操作玩家名称(isAs为false为操作者名称)
     * @note xuid 为空字符串为控制台操作
     */
    onHomeDel(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onHomeDel");
    }
    /**
     * 前往前一死亡点
     * @param {string} pluginName 
     * @param {(xuid:string,time:string,pos:PosInfo)=>boolean} cb 请尽量不要使用匿名函数
     * @note WARN!!! time不是时间戳!(推荐使用new Date(time)获取日期时间对象)
     */
    onBackToDeathPos(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onBackToDeathPos");
    }
    /**
     * 玩家死亡记录
     * @param {string} pluginName 
     * @param {(xuid:string,pos:PosInfo)=>boolean} cb 请尽量不要使用匿名函数
     */
    onBackPlayerDie(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onBackPlayerDie");
    }
    /**
     * 商店购买前(经济条件已通过)(需要手动判断物品是否存在),可拦截(Before Give
     * @param {string} pluginName 
     * @param {(xuid:string,indexStr:string,CommodityInfo:CommodityInfo,num:number)=>boolean} cb 请尽量不要使用匿名函数
     * @note indexStr:字符串路径(这玩意理论上是准的)(与UseLog是相同的)
     */
    onBeforeShopBuy(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onBeforeShopBuy");
    }
    /**
     * 商店购买后  这个监听只能拦截提示和扣钱（After Give
     * @param {string} pluginName 
     * @param {(xuid:string,indexStr:string,CommodityInfo:CommodityInfo,num:number)=>boolean} cb 请尽量不要使用匿名函数
     * @note indexStr:字符串路径(这玩意理论上是准的)(与UseLog是相同的)
     */
    onAfterShopBuy(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onAfterShopBuy");
    }
    /**
     * 商店回收前(不含物品检测),需要手动判断玩家是否拥有此物品，可拦截!（Before Clear
     * @param {string} pluginName 
     * @param {(xuid:string,indexStr:string,CommodityInfo:CommodityInfo,num:number)=>boolean} cb 请尽量不要使用匿名函数
     * @note indexStr:字符串路径(这玩意理论上是准的)(与UseLog是相同的)
     */
    onBeforeShopSell(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onBeforeShopSell");
    }
    /**
     * 商店回收后 这个监听只能拦截提示和扣钱（After Clear
     * @param {string} pluginName 
     * @param {(xuid:string,indexStr:string,CommodityInfo:CommodityInfo,num:number)=>boolean} cb 请尽量不要使用匿名函数
     * @note indexStr:字符串路径(这玩意理论上是准的)(与UseLog是相同的)
     */
    onAfterShopSell(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onAfterShopSell");
    }
    /**
     * @deprecated 将在TMET 1.3.5版本弃用,请改用 onAfterShopBuy
     */
    onShopBuy(pluginName, cb) {
        return this.onAfterShopBuy(pluginName, cb);
    }
    /**
     * @deprecated 将在TMET 1.3.5版本弃用,请改用 onAfterShopSell
     */
    onShopSell(pluginName, cb) {
        return this.onAfterShopSell(pluginName, cb);
    }
    /**
     * 改变Motd (拦截此监听不会打破Motd循环)
     * @param {string} pluginName 
     * @param {(motd:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onMotdChange(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onMotdChange");
    }
    /**
     * 尝试TPR
     * @param {string} pluginName 
     * @param {(xuid:string,x:number,z:number)=>boolean} cb 请尽量不要使用匿名函数
     * @note 此监听执行后才会传送，所以请在监听内通过玩家获取tpr之前的坐标
     */
    onTPRTry(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPRTry");
    }
    /**
     * TPR失败 不可拦截!!!
     * @param {string} pluginName 
     * @param {(xuid:string,reason:"LeftGame"|"NotFindSafePos")=>boolean} cb 请尽量不要使用匿名函数
     * @note 请不要在原因为LeftGame时获取玩家对象!
     */
    onTPRFail(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPRFail");
    }
    /**
     * TPR成功 不可拦截!!!
     * @param {string} pluginName 
     * @param {(xuid:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onTPRSuccess(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onTPRSuccess");
    }
    /**
     * 刷新区块
     * @param {string} pluginName 
     * @param {(xuid:string)=>boolean} cb 请尽量不要使用匿名函数
     */
    onRefreshChunk(pluginName, cb) {
        return FastAddEvent(pluginName, cb, "onRefreshChunk");
    }
}();

let TMETApi = new class {
    constructor() { }
    /**
     * 获取版本信息
     * @returns {{"major":number,"minor":number,"revision":number,"isBeta":boolean}}
     */
    getVersion() {
        A_SafeCheck("getVersion");
        return _A_IMPORT("getVersion")();
    }
    /**
     * 获取语言包列表
     * @returns {Array<LangPackInfo>}
     */
    getLangPackList() {
        A_SafeCheck("getLangPackList");
        return _A_IMPORT("getLangPackList")();
    }
    /**
     * 设置语言包
     * @param {(string|"")} xuid 为空字符时设置默认语言
     * @param {string} LangName 语言包名称(如"zh_CN")
     * @returns {boolean} 
     */
    setLangPack(xuid, LangName) {
        A_SafeCheck("setLangPack");
        return _A_IMPORT("setLangPack")(xuid, LangName);
    }
    /**
     * 加载语言包
     * @param {string} LangFileName 语言包文件名称(例如"zh_CN.json")(自动从语言包路径寻找) 一定要填后缀!!!
     * @returns {-1|0|1} 加载成功并且是标准包为1,加载成功并不是标准包为0,加载失败或者已加载为-1
     */
    loadLangPack(LangFileName) {
        A_SafeCheck("loadLangPack");
        return _A_IMPORT("loadLangPack")(LangFileName);
    }
    /**
     * 卸载语言包
     * @param {string} LangFileName 语言包文件名称(例如"zh_CN.json") 一定要填后缀!!!
     * @returns {boolean}
     */
    unloadLangPack(LangFileName) {
        A_SafeCheck("unloadLangPack");
        return _A_IMPORT("unloadLangPack")(LangFileName);
    }
    /**
     * 获取玩家经济
     * @param {string} realName 玩家真实名称
     * @returns {(number|null)} 为null时没有选定的玩家信息
     */
    getMoney(realName) {
        A_SafeCheck("getMoney");
        return _A_IMPORT("getMoney")(realName);
    }
    /**
     * 设置玩家经济(注意:此API会触发事件"onMoneyChange")
     * @param {string} realName 玩家真实名称
     * @param {number} val 设定值
     * @param {?("sync"|"init"|"set")} type 类型,最好使用set,init为初始化玩家经济,sync为玩家同步经济(无法拦截)(默认set
     * @param {?boolean} isTarn 是否为转账模式(为true时不记录账单)
     * @param {?string} note 备注
     * @returns {(boolean|null)} 为null时为报错,false时为其他错误(请自行排查)
     */
    setMoney(realName, val, type = "set", isTarn = false, note = "") {
        A_SafeCheck("setMoney");
        return _A_IMPORT("setMoney")(realName, val, type, isTarn, note);
    }
    /**
     * 转账(带税率)(此API会触发事件"onMoneyPay")
     * @param {string} realName 发起者玩家真实名称
     * @param {string} toRealName 接收者玩家真实名称
     * @param {number} val 转账值
     * @param {?string} note 
     * @returns {(number|null)} 税收,为null时为报错
     */
    tranMoney(realName, toRealName, val, note = "pay") {
        A_SafeCheck("tranMoney");
        return _A_IMPORT("tranMoney")(realName, toRealName, val, note);
    }
    /**
     * 获取税值
     * @returns {number} Float
     */
    getPayTax() {
        A_SafeCheck("getPayTax");
        return _A_IMPORT("getPayTax")();
    }
    /**
     * 获取账单
     * @param {string} xuid 玩家xuid
     * @param {?number} time 要查询几秒前的数据(为-1时获取所有账单数据)
     * @returns {(HistoryInfo[]|null)} 为null时表示完全没有数据
     */
    getHistory(xuid, time = -1) {
        A_SafeCheck("getHistory");
        return _A_IMPORT("getHistory")(xuid, time);
    }
    /**
     * 删除账单
     * @param {string} xuid 玩家 xuid
     * @return {boolean} 是否成功
     */
    clearHistory(xuid) {
        A_SafeCheck("clearHistory");
        return _A_IMPORT("clearHistory")(xuid);
    }
    /**
     * 获取经济名称
     * @returns {string}
     */
    getMoneyName() {
        A_SafeCheck("getMoneyName");
        return _A_IMPORT("getMoneyName")();
    }
    /**
     * 获取经济类型
     * @returns {("score"|"llmoney")} 获取经济类型
     */
    getMoneyType() {
        A_SafeCheck("getMoneyType");
        return _A_IMPORT("getMoneyType")();
    }
    /**
     * 获取TPA实例信息
     * @param {string} xuid 相关玩家xuid(可以是发起者,也可以是接收者)
     * @returns {(TPAInfo|null)} 为null时为没有信息
     */
    getTPAData(xuid) {
        A_SafeCheck("getTPAData");
        return _A_IMPORT("getTPAData")(xuid);
    }
    /**
     * 删除TPA实例
     * @param {string} xuid 相关玩家xuid(可以是发起者,也可以是接收者)
     * @returns {boolean}
     */
    delTPAData() {
        A_SafeCheck("delTPAData");
        return _A_IMPORT("delTPAData")();
    }
    /**
     * 获取传送点信息
     * @param {string} 传送点名称
     * @returns {(PosInfo|null)} 为null时为没有信息
     */
    getWARPData(warpName) {
        A_SafeCheck("getWARPData");
        return _A_IMPORT("getWARPData")(warpName);
    }
    /**
     * 设置传送点信息
     * @param {string} 传送点名称
     * @param {(PosInfo|Pos)} 可以直接传递pos,object
     * @returns {boolean}
     */
    setWARPData(warpName, posInfo) {
        A_SafeCheck("setWARPData");
        return _A_IMPORT("setWARPData")(warpName, posInfo);
    }
    /**
     * 获取家数据
     * @param {string} playerName 玩家名
     * @param {string} homeName 家名称
     * @returns {(PosInfo|null)}
     */
    getHomeData(playerName, homeName) {
        A_SafeCheck("getHomeData");
        return _A_IMPORT("getHomeData")(playerName, homeName);
    }
    /**
     * 设置玩家家信息
     * @param {string} playerName 玩家名
     * @param {string} homeName 家名称
     * @param {(PosInfo|Pos)} pos 可以直接传递pos,object
     * @returns {boolean}
     */
    setHomeData(playerName, homeName, pos) {
        A_SafeCheck("setHomeData");
        return _A_IMPORT("setHomeData")(playerName, homeName, pos);
    }
    /**
     * 获取死亡信息列表
     * @param {string} realName 玩家名
     * @returns {(DeathInfo[]|null)}
     */
    getDeathData(realName) {
        A_SafeCheck("getDeathData");
        return _A_IMPORT("getDeathData")(realName);
    }
    /**
     * 获取公告信息
     * @returns {NoticeInfo}
     */
    getNoticeData() {
        A_SafeCheck("getNoticeData");
        return _A_IMPORT("getNoticeData")();
    }
    /**
     * 设置公告信息
     * @param {(NoticeInfo|{"Title":?string,"Content":?string})} 公告信息(可以传递对象)
     * @returns {boolean}
     */
    setNoticeData(noticeData) {
        A_SafeCheck("setNoticeData");
        return _A_IMPORT("setNoticeData")(noticeData);
    }
    /**
     * 获取商店数据
     * @returns {(ShopExamInfo[]|ShopGroupInfo[])}
     */
    getShopData() {
        A_SafeCheck("getShopData");
        return _A_IMPORT("getShopData")();
    }
    /**
     * 设置商店数据
     * @param {(ShopExamInfo[]|ShopGroupInfo[])} obj 请使用object!
     * @returns {boolean}
     */
    setShopData(obj) {
        A_SafeCheck("setShopData");
        return _A_IMPORT("setShopData")(obj);
    }
};

globalThis.TPAInfo = TPAInfo;
globalThis.PosInfo = PosInfo;
globalThis.LangPackInfo = LangPackInfo;
globalThis.CommodityInfo = CommodityInfo;
globalThis.NoticeInfo = NoticeInfo;
globalThis.DeathInfo = DeathInfo;
globalThis.ShopExamInfo = ShopExamInfo;
globalThis.ShopGroupInfo = ShopGroupInfo;

globalThis.TMETEvent = TMETEvent;
globalThis.TMETApi = TMETApi;